#include "window.h"
#include <vector>
#include <iostream>

#include "objectTreeItem.h"
#include "vertexTreeItem.h"

Window::Window(){
	setWindowTitle(tr("[GC] Vizualizador"));
    statusBar()->showMessage(tr(""));
    setUnifiedTitleAndToolBarOnMac(true);

	canvas = new GLWidget(this);
	setCentralWidget(canvas);

	tree = NULL;

	createPanels();
	createActions();
	createMenus();
}

Window::~Window(){
	if(canvas) delete canvas;
}

void Window::createMenus(){
    QMenu *fileMenu;
    QMenu *editMenu;
    QMenu *viewMenu;
    QMenu *importMenu;
    QMenu *exportMenu;

    fileMenu = menuBar()->addMenu(QString::fromUtf8("&Arquivo"));
    fileMenu->addAction(newSceneAct);
    fileMenu->addSeparator();
    fileMenu->addAction(openSceneAct);
    fileMenu->addAction(openObjectAct);
    fileMenu->addSeparator();
    fileMenu->addAction(saveSceneAct);
    fileMenu->addAction(saveObjectAct);
    fileMenu->addSeparator();

    importMenu = fileMenu->addMenu(QString::fromUtf8("&Importar"));
    importMenu->addAction(importInAct);
    importMenu->addAction(importOutAct);
//    importMenu->addAction(importWedAct);

    exportMenu = fileMenu->addMenu(QString::fromUtf8("E&xportar"));
    exportMenu->addAction(exportInAct);
    exportMenu->addAction(exportOutAct);
    exportMenu->addAction(exportWedAct);

    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);

    editMenu = menuBar()->addMenu(QString::fromUtf8("&Editar"));
    editMenu->addAction(deleteObjectAct);
    editMenu->addSeparator();
    editMenu->addAction(convexHullAct);
    editMenu->addAction(selectConvexHullVerticesAct);
    editMenu->addSeparator();
    editMenu->addAction(tetrahedralizationAct);

    viewMenu = menuBar()->addMenu(QString::fromUtf8("&Vizualizar"));
//    viewMenu->addAction(showStepByStepAct);
//    viewMenu->addSeparator();
    viewMenu->addAction(showAxisAct);
    viewMenu->addAction(showGridAct);
    viewMenu->addAction(showVerticesAct);
    viewMenu->addAction(showEdgesAct);
    viewMenu->addAction(showWireframeAct);
    viewMenu->addAction(showOpaqueAct);
    viewMenu->addAction(showNormalAct);
    viewMenu->addSeparator();
    viewMenu->addAction(viewTreePanelAct);
    viewMenu->addAction(viewVerticesPanelAct);
}

void Window::createPanels(){
    treePanelDock = new QDockWidget(tr("Objetos"), this);

    tree = new QTreeWidget(treePanelDock);
    tree->setHeaderHidden(true);
    tree->setExpandsOnDoubleClick(false);

	treePanelDock->setWidget(tree);
    treePanelDock->setMaximumWidth(300);
    addDockWidget(Qt::RightDockWidgetArea, treePanelDock);

    QTreeWidgetItem *headerItem = tree->headerItem();
    headerItem->setText(0, QApplication::translate("Dialog", "Objetos", 0, QApplication::UnicodeUTF8));

    connect(tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(selectTreeItem(QTreeWidgetItem *, int)));

    verticesPanelDock = new QDockWidget(tr("Adicionar vertices"), this);

    verticesPanel = new VerticesPanel(verticesPanelDock, canvas->getScene());

	verticesPanelDock->setWidget(verticesPanel);
    verticesPanelDock->setMaximumWidth(300);
    addDockWidget(Qt::RightDockWidgetArea, verticesPanelDock);

    connect(verticesPanel, SIGNAL(done(Object *)), this, SLOT(createVertices(Object *)));
}

void Window::createActions(){
    newSceneAct = new QAction(QString::fromUtf8("Nova Cena"), this);
    newSceneAct->setShortcuts(QKeySequence::New);
    newSceneAct->setStatusTip(QString::fromUtf8("Criar uma nava cena sem objetos."));
    connect(newSceneAct, SIGNAL(triggered()), this, SLOT(newScene()));
    connect(newSceneAct, SIGNAL(triggered()), this, SLOT(updateActions()));

    openSceneAct = new QAction(QString::fromUtf8("Abrir Cena"), this);
    openSceneAct->setShortcuts(QKeySequence::Open);
    openSceneAct->setStatusTip(QString::fromUtf8("Abrir uma nava cena."));
    connect(openSceneAct, SIGNAL(triggered()), this, SLOT(openScene()));
    connect(openSceneAct, SIGNAL(triggered()), this, SLOT(updateActions()));

    openObjectAct = new QAction(QString::fromUtf8("Abrir Objeto"), this);
//    openObjectAct->setShortcuts();
    openObjectAct->setStatusTip(QString::fromUtf8("Abrir um novo objeto na cena corrente."));
    connect(openObjectAct, SIGNAL(triggered()), this, SLOT(openObject()));
    connect(openObjectAct, SIGNAL(triggered()), this, SLOT(updateActions()));

    saveSceneAct = new QAction(QString::fromUtf8("Salvar Cena"), this);
    saveSceneAct->setShortcuts(QKeySequence::Save);
    saveSceneAct->setStatusTip(QString::fromUtf8("Salvar cena atual."));
    saveSceneAct->setEnabled(false);
    connect(saveSceneAct, SIGNAL(triggered()), this, SLOT(saveScene()));

    saveObjectAct = new QAction(QString::fromUtf8("Salvar Objeto"), this);
//    saveObjectAct->setShortcuts(QKeySequence::Save);
    saveObjectAct->setStatusTip(QString::fromUtf8("Salvar objeto selecionado."));
    saveObjectAct->setEnabled(false);
    connect(saveObjectAct, SIGNAL(triggered()), this, SLOT(saveObject()));

    importInAct = new QAction(QString::fromUtf8("Input *.in"), this);
    importInAct->setStatusTip(QString::fromUtf8("Importar arquivos input (*.in)."));
    connect(importInAct, SIGNAL(triggered()), this, SLOT(importIn()));
    connect(importInAct, SIGNAL(triggered()), this, SLOT(updateActions()));

    importOutAct = new QAction(QString::fromUtf8("Output *.out"), this);
    importOutAct->setStatusTip(QString::fromUtf8("Importar arquivos output(*.out)."));
    connect(importOutAct, SIGNAL(triggered()), this, SLOT(importOut()));
    connect(importOutAct, SIGNAL(triggered()), this, SLOT(updateActions()));
//
//    importWedAct = new QAction(QString::fromUtf8("*.wed"), this);
//    importWedAct->setStatusTip(QString::fromUtf8("Importar arquivos winged-edge(*.wed)."));
//    connect(importWedAct, SIGNAL(triggered()), this, SLOT(importWed()));
//    connect(importWedAct, SIGNAL(triggered()), this, SLOT(updateActions()));

    exportInAct = new QAction(QString::fromUtf8("Input *.in"), this);
    exportInAct->setStatusTip(QString::fromUtf8("Exportar arquivos input (*.in)."));
    connect(exportInAct, SIGNAL(triggered()), this, SLOT(exportIn()));

    exportOutAct = new QAction(QString::fromUtf8("Output *.out"), this);
    exportOutAct->setStatusTip(QString::fromUtf8("Exportar arquivos output (*.out)."));
    connect(exportOutAct, SIGNAL(triggered()), this, SLOT(exportOut()));

    exportWedAct = new QAction(QString::fromUtf8("Winged-Edge *.wed"), this);
    exportWedAct->setStatusTip(QString::fromUtf8("Exportar arquivos winged-edge (*.wed)."));
    connect(exportWedAct, SIGNAL(triggered()), this, SLOT(exportWed()));

    exitAct = new QAction(QString::fromUtf8("Sair"), this);
    exitAct->setShortcuts(QKeySequence::Quit);
    exitAct->setStatusTip(QString::fromUtf8("Sair da aplicação."));
    connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));

    deleteObjectAct = new QAction(QString::fromUtf8("Deletar objeto"), this);
//    deleteObjectAct->setShortcuts();
    deleteObjectAct->setEnabled(false);
    deleteObjectAct->setStatusTip(QString::fromUtf8("Deletar objetos selecionados."));
    connect(deleteObjectAct, SIGNAL(triggered()), this, SLOT(deleteObject()));

    convexHullAct = new QAction(QString::fromUtf8("Convex Hull"), this);
//    convexHullAct->setShortcuts();
    convexHullAct->setEnabled(false);
    convexHullAct->setStatusTip(QString::fromUtf8("Achar o fecho convexo dos objetos selecionados."));
    connect(convexHullAct, SIGNAL(triggered()), this, SLOT(convexHull()));

    tetrahedralizationAct = new QAction(QString::fromUtf8("Tetraedralização"), this);
//    tetrahedralizationAct->setShortcuts();
    tetrahedralizationAct->setEnabled(false);
    tetrahedralizationAct->setStatusTip(QString::fromUtf8("Achar os tetraedros do objeto."));
    connect(tetrahedralizationAct, SIGNAL(triggered()), this, SLOT(tetrahedralization()));

    showAxisAct = new QAction(QString::fromUtf8("Eixos"), this);
    showAxisAct->setCheckable(true);
    showAxisAct->setChecked(canvas->isShowingAxis());
//    showAxisAct->setShortcuts();
    showAxisAct->setStatusTip(QString::fromUtf8("Mostrar eixos."));
    connect(showAxisAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowAxis(bool)));

    showGridAct = new QAction(QString::fromUtf8("Grade"), this);
    showGridAct->setCheckable(true);
    showGridAct->setChecked(canvas->isShowingGrid());
//    showGridAct->setShortcuts();
    showGridAct->setStatusTip(QString::fromUtf8("Mostrar grade."));
    connect(showGridAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowGrid(bool)));

    showVerticesAct = new QAction(QString::fromUtf8("Vertices"), this);
    showVerticesAct->setCheckable(true);
    showVerticesAct->setChecked(canvas->isShowingVertices());
//    showVerticesAct->setShortcuts();
    showVerticesAct->setStatusTip(QString::fromUtf8("Mostrar vertices."));
    connect(showVerticesAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowVertices(bool)));

    showEdgesAct = new QAction(QString::fromUtf8("Arestas"), this);
    showEdgesAct->setCheckable(true);
    showEdgesAct->setChecked(canvas->isShowingEdges());
//    showEdgesAct->setShortcuts();
    showEdgesAct->setStatusTip(QString::fromUtf8("Mostrar arestas."));
    connect(showEdgesAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowEdges(bool)));
    connect(showEdgesAct, SIGNAL(toggled(bool)), this, SLOT(updateWireframeAction(bool)));

    showWireframeAct = new QAction(QString::fromUtf8("Wireframe"), this);
    showWireframeAct->setCheckable(true);
    showWireframeAct->setChecked(canvas->isShowingWireframe());
    showWireframeAct->setEnabled(false);
//    showWireframeAct->setShortcuts();
    showWireframeAct->setStatusTip(QString::fromUtf8("Mostrar em wireframe."));
    connect(showWireframeAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowWireframe(bool)));
    connect(showWireframeAct, SIGNAL(toggled(bool)), this, SLOT(updateWireframeAction(bool)));

    showOpaqueAct = new QAction(QString::fromUtf8("Opaco"), this);
    showOpaqueAct->setCheckable(true);
    showOpaqueAct->setChecked(canvas->isShowingOpaque());
//    showOpaqueAct->setShortcuts();
    showOpaqueAct->setStatusTip(QString::fromUtf8("Mostrar tudo em opaco."));
    connect(showOpaqueAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowOpaque(bool)));

    showNormalAct = new QAction(QString::fromUtf8("Normal da face"), this);
    showNormalAct->setCheckable(true);
    showNormalAct->setChecked(canvas->isShowingNormal());
//    showNormalAct->setShortcuts();
    showNormalAct->setStatusTip(QString::fromUtf8("Mostrar as normais das faces."));
    connect(showNormalAct, SIGNAL(toggled(bool)), canvas, SLOT(changeShowNormal(bool)));
//
//    showStepByStepAct = new QAction(QString::fromUtf8("Passo a passo"), this);
//    showStepByStepAct->setCheckable(true);
//    showStepByStepAct->setChecked(canvas->isShowingStepByStep());
////    showStepByStepAct->setShortcuts();
//    showStepByStepAct->setStatusTip(QString::fromUtf8("Mostrar fecho convexo passo a passo."));
//    connect(showStepByStepAct, SIGNAL(toggled(bool)), canvas, SLOT(changeStepByStepOpaque(bool)));

    selectConvexHullVerticesAct = new QAction(QString::fromUtf8("Selecionar vertices do fecho"), this);
//    showStepByStepAct->setShortcuts();
    selectConvexHullVerticesAct->setEnabled(false);
    selectConvexHullVerticesAct->setStatusTip(QString::fromUtf8("Selecionar os somente os vertices do fecho convexo do(s) objeto(s) selecionados."));
    connect(selectConvexHullVerticesAct, SIGNAL(triggered()), this, SLOT(selectConvexHullVertices()));

	viewTreePanelAct = treePanelDock->toggleViewAction();

	viewVerticesPanelAct = verticesPanelDock->toggleViewAction();
}

void Window::updateWireframeAction(bool){
    if(canvas->isShowingEdges())
        showWireframeAct->setEnabled(true);
    else{
        showWireframeAct->setEnabled(false);
        canvas->changeShowWireframe(false);
    }
}

void Window::updateActions(){
    QTreeWidgetItem *rootItem = tree->invisibleRootItem();
    bool enable = (rootItem->childCount() > 0);

    saveSceneAct->setEnabled(enable);
    saveObjectAct->setEnabled(enable);
    convexHullAct->setEnabled(enable);
    tetrahedralizationAct->setEnabled(enable);
    exportInAct->setEnabled(enable);
    exportOutAct->setEnabled(enable);
    exportWedAct->setEnabled(enable);
    deleteObjectAct->setEnabled(enable);
    if(!enable) selectConvexHullVerticesAct->setEnabled(false);
}

void Window::updateTree(Object *_obj){
    QTreeWidgetItem *rootItem = tree->invisibleRootItem();

    ObjectTreeItem *objectTreeItem;
    for(int i = 0; i < rootItem->childCount(); i++){
        objectTreeItem = (ObjectTreeItem *) rootItem->child(i);
        if (_obj == objectTreeItem->getObject()){
            objectTreeItem->updateObject(_obj);
            return;
        }
    }

    objectTreeItem = new ObjectTreeItem(rootItem, _obj, this);
}

void Window::updateAllTree(){
    QTreeWidgetItem *rootItem = tree->invisibleRootItem();

    ObjectTreeItem *objectTreeItem;
    for(int i = 0; i < rootItem->childCount(); i++){
        objectTreeItem = (ObjectTreeItem *) rootItem->child(i);
        objectTreeItem->updateObject();
    }
}

void Window::selectTreeItem(QTreeWidgetItem *_item, int){
    ((TreeItem *)_item)->select();
    canvas->updateGL();
}

void Window::selectTetrahedron(Tetrahedron *_t){
    canvas->getScene()->selectTetrahedron(_t);
}

void Window::selectVertex(Vertex *_v){
    canvas->getScene()->selectVertex(_v);
}

void Window::selectEdge(Edge *_e){
    canvas->getScene()->selectEdge(_e);
}

void Window::selectFace(Face *_f){
    canvas->getScene()->selectFace(_f);
}

void Window::selectObject(Object *_o){
    canvas->getScene()->selectObject(_o);
}

void Window::newScene(){
    verticesPanel->setScene(canvas->newScene());
    tree->clear();
    selectConvexHullVerticesAct->setEnabled(false);
}

void Window::openScene(){
    tree->clear();

	QString filename = QFileDialog::getOpenFileName(this, tr("Abrir cena"), QDir::currentPath(),tr("Arquivo de cenas (*.scene)"));

    if (!filename.isEmpty()){
        qDebug() << "abrindo: " << filename;
        QFile* file = new QFile(filename);
        file->open(QFile::ReadOnly);
        std::vector<Object *> objects = canvas->loadScene(file);

        //colocar atualizar a lista de objetos
        for(unsigned int i = objects.size(); i > 0; i--){
            updateTree(objects.at(i - 1));
        }
	}
	selectConvexHullVerticesAct->setEnabled(false);
	verticesPanel->setScene(canvas->getScene());
}

void Window::openObject(){
	QString filename = QFileDialog::getOpenFileName(this, tr("Abrir objeto"), QDir::currentPath(),tr("Arquivo de objetos (*.objt)"));

    if (!filename.isEmpty()){
        qDebug() << "abrindo: " << filename;
        QFile* file = new QFile(filename);
        file->open(QFile::ReadOnly);
        Object *object = canvas->loadObject(file);

        //colocar atualizar o objeto
        updateTree(object);
	}
}

void Window::createVertices(Object *_o){
    canvas->updateGL();
    updateTree(_o);
    updateActions();

    selectConvexHullVerticesAct->setEnabled(false);
}

void Window::saveScene(){
	QString filename = QFileDialog::getSaveFileName(this, tr("Salvar cena"), QDir::currentPath(), tr("Arquivo de cenas (*.scene)"));

    if (filename.isEmpty()){
        return;
    }

    if (QFileInfo(filename).suffix().isEmpty())
        filename += ".scene";

    QFile* file = new QFile(filename);
    file->open(QFile::WriteOnly);
    canvas->saveScene(file);
}

void Window::saveObject(){
	QString filename = QFileDialog::getSaveFileName(this, tr("Salvar objeto"), QDir::currentPath(), tr("Arquivo de objetos (*.objt)"));

    if (filename.isEmpty()){
        return;
    }

    if (QFileInfo(filename).suffix().isEmpty())
        filename += ".objt";

    QFile* file = new QFile(filename);
    file->open(QFile::WriteOnly);
    canvas->saveObject(file);
}

void Window::importIn(){
    tree->clear();

	QString filename = QFileDialog::getOpenFileName(this, tr("Importar input"), QDir::currentPath(),tr("Arquivo de input (*.in)"));

    if (!filename.isEmpty()){
        qDebug() << "abrindo: " << filename;
        QFile* file = new QFile(filename);
        file->open(QFile::ReadOnly);
        std::vector<Object *> objects = canvas->loadIn(file);

        //colocar atualizar a lista de objetos
        for(unsigned int i = objects.size(); i > 0; i--){
            updateTree(objects.at(i - 1));
        }
	}
	selectConvexHullVerticesAct->setEnabled(false);
	verticesPanel->setScene(canvas->getScene());
}

void Window::importOut(){
    tree->clear();

	QString filename = QFileDialog::getOpenFileName(this, tr("Importar output"), QDir::currentPath(),tr("Arquivo de output (*.out)"));

    if (!filename.isEmpty()){
        qDebug() << "abrindo: " << filename;
        QFile* file = new QFile(filename);
        file->open(QFile::ReadOnly);
        std::vector<Object *> objects = canvas->loadOut(file);

        //colocar atualizar a lista de objetos
        for(unsigned int i = objects.size(); i > 0; i--){
            updateTree(objects.at(i - 1));
        }
	}
	selectConvexHullVerticesAct->setEnabled(false);
	verticesPanel->setScene(canvas->getScene());
}

void Window::exportIn(){
	QString filename = QFileDialog::getSaveFileName(this, tr("Exportar input"), QDir::currentPath(), tr("Arquivo de input (*.in)"));

    if (filename.isEmpty()){
        return;
    }

    if (QFileInfo(filename).suffix().isEmpty())
        filename += ".in";

    QFile* file = new QFile(filename);
    file->open(QFile::WriteOnly);
    canvas->saveIn(file);
}

void Window::exportOut(){
	QString filename = QFileDialog::getSaveFileName(this, tr("Exportar output "), QDir::currentPath(), tr("Arquivo de output (*.out)"));

    if (filename.isEmpty()){
        return;
    }

    if (QFileInfo(filename).suffix().isEmpty())
        filename += ".out";

    QFile* file = new QFile(filename);
    file->open(QFile::WriteOnly);
    canvas->saveOut(file);
}

void Window::exportWed(){
	QString filename = QFileDialog::getSaveFileName(this, tr("Exportar winged-edge"), QDir::currentPath(), tr("Arquivo de winged-edge (*.wed)"));

    if (filename.isEmpty()){
        return;
    }

    if (QFileInfo(filename).suffix().isEmpty())
        filename += ".wed";

    QFile* file = new QFile(filename);
    file->open(QFile::WriteOnly);
    canvas->saveWed(file);
}

void Window::deleteObject(){
    QTreeWidgetItem *rootItem = tree->invisibleRootItem();
    ObjectTreeItem *objectTreeItem;
    TreeItem *treeItem;
    Vertex *v;
    VertexTreeItem *vertexTreeItem;

    for(int i = 0; i < rootItem->childCount(); i++){
        objectTreeItem = (ObjectTreeItem *) rootItem->child(i);

        if(objectTreeItem->isSelected()){
            //decelecionando qualquer vertice dess eobjeto que esteja selecionado

            treeItem = (TreeItem *)objectTreeItem->child(2);
            for(int j = treeItem->childCount(); j > 0 ; j--){
                vertexTreeItem = (VertexTreeItem *) treeItem->child(j - 1);
                v = vertexTreeItem->getVertex();
                if(v->isSelected()){
                    v->select(false);
                    selectVertex(v);
                }
            }

            //removendo o objeto em si
            rootItem->removeChild(objectTreeItem);
            delete objectTreeItem;

            canvas->getScene()->deleteObject(objectTreeItem->getObject());
        }
    }
    canvas->updateGL();
}

void Window::convexHull(){
    canvas->convexHull();
    selectConvexHullVerticesAct->setEnabled(true);
    updateAllTree();
}

void Window::tetrahedralization(){
    canvas->tetrahedralization();
    updateAllTree();
}

void Window::selectConvexHullVertices(){
    std::vector<Vertex *> vertices = canvas->getScene()->selectConvexHullVertices();
    Vertex *v;

    QTreeWidgetItem *rootItem = tree->invisibleRootItem();
    ObjectTreeItem *objectTreeItem;
    TreeItem *treeItem;
    VertexTreeItem *vertexTreeItem;

    for(int i = 0; i < rootItem->childCount(); i++){
        objectTreeItem = (ObjectTreeItem *) rootItem->child(i);
        if(!objectTreeItem->isSelected()) continue;
        treeItem = (TreeItem *)objectTreeItem->child(2);
        for(int j = treeItem->childCount(); j > 0 ; j--){
            vertexTreeItem = (VertexTreeItem *) treeItem->child(j - 1);
            v = vertexTreeItem->getVertex();
            v->select(true);
            vertexTreeItem->select();
        }
    }

    bool finded = false;
    while(!vertices.empty()){
        v = vertices.back();

        for(int i = 0; i < rootItem->childCount(); i++){
            objectTreeItem = (ObjectTreeItem *) rootItem->child(i);
            if(!objectTreeItem->isSelected()) continue;
            treeItem = (TreeItem *)objectTreeItem->child(2);
            for(int j = treeItem->childCount(); j > 0 ; j--){
                vertexTreeItem = (VertexTreeItem *) treeItem->child(j - 1);
                if(vertexTreeItem->getVertex() == v){
                    vertexTreeItem->select();
                    finded = true;
                    break;
                }
            }
            if(finded) break;
        }
        if(!finded) qDebug() << "não foi achado! ... estranho o.o!";
        finded = false;
        vertices.pop_back();
    }
    canvas->updateGL();
}

void Window::keyPressEvent(QKeyEvent *_event){
     if (_event->key() == Qt::Key_Escape)
        close();
}
